package com.exception;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.hibernate.exception.ConstraintViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import org.springframework.web.servlet.support.RequestContextUtils;

/**
 * ExceptionResolver
 * 
 * 2013-7-26 上午10:49:27
 * 
 * @author <a href="xinyuan.guo@baozun.cn">郭馨元</a>
 * 
 */
public class ExceptionResolver extends SimpleMappingExceptionResolver {

	private static final Logger log = LoggerFactory.getLogger(ExceptionResolver.class);

	private static final String KEY_STATUS = "result";
	private static final String KEY_MSG = "msg";

	public static final String BUSINESS_EXCEPTION_PREFIX = "business_exception_";

	@Resource
	private ApplicationContext context;

	@Override
	protected ModelAndView getModelAndView(String viewName, Exception exception, HttpServletRequest request) {
		ModelAndView mv;
		String message;
		int errorCode;

		Locale locale = RequestContextUtils.getLocale(request);

		if (exception instanceof BindException) {
			List<FieldError> fieldErrors = ((BindException) exception).getFieldErrors();
			List<String> innerMsgList = new ArrayList<String>();
			if (CollectionUtils.isNotEmpty(fieldErrors))
				for (FieldError fieldError : fieldErrors)
					innerMsgList.add(fieldError.getField() + ":" + fieldError.getRejectedValue());
			String innerMsg = StringUtils.join(innerMsgList, ',');
			errorCode = ShopErrorCodes.ERR_BINDEXCEPTION;
			message = getContextMsg(BUSINESS_EXCEPTION_PREFIX + errorCode, locale, innerMsg);
			log.error("Unexpected exception :", exception);
		} else if (exception instanceof ConstraintViolationException) {
			errorCode = ShopErrorCodes.ERR_FORIGNKEY;
			message = getContextMsg(BUSINESS_EXCEPTION_PREFIX + errorCode, locale,
					((ConstraintViolationException) exception).getMessage());
		} else if (exception instanceof DataIntegrityViolationException) {
			errorCode = ShopErrorCodes.ERR_FORIGNKEY;
			message = getContextMsg(BUSINESS_EXCEPTION_PREFIX + errorCode, locale,
					((DataIntegrityViolationException) exception).getRootCause());
		} else if (exception instanceof BusinessException) {
			BusinessException bExc = (BusinessException) exception;
			errorCode = bExc.getErrorCode();
			if (errorCode == 0)
				message = exception.getMessage();
			else
				message = getContextMsg(BUSINESS_EXCEPTION_PREFIX + errorCode, locale, bExc.getArgs());
		} else {
			log.error("Unexpected exception :", exception);
			message = exception.getMessage();
			errorCode = -1;
		}

		String ip = request.getHeader("X-Real-IP");
		if(StringUtils.isEmpty(ip)) {
			ip = request.getRemoteAddr();
		}
		
		log.info("[" + ip + "]" + errorCode + ":" + message);

		if (request.getHeader("X-Requested-With") != null) {
			mv = new ModelAndView("json");
		} else {
			mv = super.getModelAndView(viewName, exception, request);
		}
		mv.getModelMap().put(ExceptionResolver.KEY_STATUS, errorCode);
		mv.getModelMap().put(ExceptionResolver.KEY_MSG, message);
		return mv;
	}

	/**
	 * 读取国际化资源文件中的对应信息
	 */
	private String getContextMsg(String key, Locale locale, Object... innerMsg) {
		return context.getMessage(key, innerMsg, key, locale);
	}
}
